import random
from pathlib import Path

import networkx as nx
from pyvis.network import Network

from utils import Base


class Parser(Base):
    def __init__(self):
        super().__init__()
        self.graph = nx.DiGraph()

    def print_dependency_edge(self):
        graph = self.graph
        print("依赖关系图的边：")
        for source, target in graph.edges():
            print(f"{source} -> {target}")

    def print_adjacency_list(self):
        graph = self.graph
        print("依赖关系图的邻接表：")
        for node in graph.nodes():
            dependencies = list(graph.successors(node))
            if dependencies:
                print(f"{node} 依赖于: {', '.join(dependencies)}")
            else:
                print(f"{node} 没有依赖任何文件")

    def get_adjacency_list(self):
        graph = self.graph
        adj = {}
        for node in graph.nodes():
            dependencies = list(graph.successors(node))
            if dependencies:
                adj[node] = dependencies
            else:
                adj[node] = []
        return adj

    def get_node_adjacency_list(self, node):
        graph = self.graph
        dependencies = list(graph.successors(node))
        if dependencies:
            return dependencies
        return []

    @staticmethod
    def _print_hierarchical_dependencies(graph, node, visited=None, level=0):
        if visited is None:
            visited = set()
        indent = '    ' * level
        print(f"{indent}{node}")
        if node not in visited:
            visited.add(node)
            for successor in graph.successors(node):
                Parser._print_hierarchical_dependencies(graph, successor, visited, level + 1)

    def print_hierarchical(self):
        dependency_graph = self.graph
        roots = [node for node in dependency_graph.nodes() if dependency_graph.in_degree(node) == 0]
        print("\n层次结构的依赖关系：")
        for root in roots:
            self._print_hierarchical_dependencies(dependency_graph, root)

    def print_parallel_level(self):
        dependency_graph = self.graph
        try:
            cycles = list(nx.find_cycle(dependency_graph, orientation='original'))
            print("发现循环依赖，无法确定并行编译的文件：")
            for edge in cycles:
                print(f"{edge[0]} -> {edge[1]}")
        except nx.NetworkXNoCycle:
            # 分配层级
            levels = self._assign_levels()
            # 获取并行编译的文件组
            compilation_groups = self.get_parallel_compilation_groups(levels)
            # 输出编译组
            print("可以并行编译的单元：")
            for level in sorted(compilation_groups.keys()):
                files = compilation_groups[level]
                print(f"Level {level}: {', '.join(files)}")

    def get_parallel_level(self):
        levels = self._assign_levels()
        compilation_groups = self.get_parallel_compilation_groups(levels)
        file_level = []
        for level in sorted(compilation_groups.keys())[::-1]:
            files = compilation_groups[level]
            file_level.append(files)
        return file_level

    def get_compilation_order(self):
        try:
            order = list(nx.topological_sort(self.graph))
            return order
        except nx.NetworkXUnfeasible:
            print("依赖关系图中存在循环，无法进行拓扑排序。")
            return None

    def get_compilation_groups(self):
        adj = self.get_adjacency_list()
        set_adj = {k: set(v) for k, v in adj.items()}
        done = set()
        groups = []
        while len(set_adj) != 0:
            set_adj = {k: v - done for k, v in set_adj.items()}
            group = []
            for file in list(set_adj.keys()):
                if len(set_adj[file]) == 0:
                    group.append(file)
                    set_adj.pop(file)
                    done.add(file)
            groups.append(group)
        return groups

    def _assign_levels(self):
        graph = self.graph
        levels = {node: None for node in graph.nodes()}
        level = 0
        current_level_nodes = [node for node in graph.nodes() if graph.in_degree(node) == 0]
        while current_level_nodes:
            next_level_nodes = []
            for node in current_level_nodes:
                levels[node] = level
            for node in current_level_nodes:
                for successor in graph.successors(node):
                    if levels[successor] is None and all(levels[predecessor] is not None for predecessor in graph.predecessors(successor)):
                        next_level_nodes.append(successor)
            current_level_nodes = list(set(next_level_nodes))
            level += 1
        return levels

    @staticmethod
    def get_parallel_compilation_groups(levels):
        from collections import defaultdict
        compilation_groups = defaultdict(list)
        for file, level in levels.items():
            compilation_groups[level].append(file)
        return compilation_groups

    def visualize(self, output_prefix: str, origin_graph=False):
        net = self._create_colored_network_auto(origin_graph)

        net = self._custom_visual_network(net)

        html_content = net.generate_html(notebook=True)
        with open(f'{output_prefix}.html', 'w', encoding='utf-8') as f:
            f.write(html_content)

    def _custom_visual_network(self, net):
        net.set_edge_smooth('continuous')

        # net.barnes_hut(
        #     gravity=-2000,  # 负值会增加节点间距
        #     central_gravity=0.3,  # 控制向中心的引力
        #     spring_length=200,  # 弹簧自然长度
        #     spring_strength=0.05,  # 弹簧强度
        #     damping=0.09  # 阻尼系数
        # )

        net.force_atlas_2based(
            gravity=-50,
            central_gravity=0.01,
            spring_length=100,
            spring_strength=0.08,
            damping=0.4,
            overlap=0.5
        )
        return net

    @staticmethod
    def _get_color_map(items):
        """为一组项目自动生成颜色映射"""
        # 预定义一些好看的颜色
        base_colors = [
            '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEEAD',
            '#D4A5A5', '#9EC1CF', '#CC99C9', '#9EE09E', '#FEB144'
        ]

        # 如果类型数量超过预定义颜色，则随机生成更多颜色
        colors = base_colors.copy()
        while len(colors) < len(items):
            # 生成随机的十六进制颜色
            new_color = '#{:06x}'.format(random.randint(0, 0xFFFFFF))
            if new_color not in colors:  # 避免重复
                colors.append(new_color)

        return dict(zip(items, colors))

    # 使用自动颜色映射
    def _create_colored_network_auto(self, origin_graph=False):
        if origin_graph:
            graph = getattr(self, 'origin_graph')
        else:
            graph = self.graph
        net = Network(notebook=True, cdn_resources='in_line', height="1500px", width="100%", directed=True)
        net.from_nx(graph)

        # 获取所有节点和边的类型
        node_types = set(nx.get_node_attributes(graph, 'type').values())
        edge_types = set(nx.get_edge_attributes(graph, 'type').values())

        if len(node_types) > 0:
            # 自动生成颜色映射
            node_colors = self._get_color_map(node_types)
            for node_id in net.get_nodes():
                node = net.get_node(node_id)
                node_color = node_colors[node['type']]
                node['color'] = node_color
                if 'title' not in node:
                    node['title'] = node['type']
                node.pop('size')

        if len(edge_types) > 0:
            # 自动生成颜色映射
            edge_colors = self._get_color_map(edge_types)
            for edge in net.get_edges():
                edge_color = edge_colors[edge['type']]
                edge['color'] = edge_color
                if 'title' not in edge:
                    edge['title'] = edge['type']

        return net

    def save_graph(self, output_prefix: str):
        nx.write_gexf(self.graph, f'{output_prefix}.gexf')

    def load_graph(self, file: Path | str):
        self.graph = nx.read_gexf(file)

    # claude的实现, 和之前的结果一样
    # def get_parallel_processing_levels(self) -> List[Set[str]]:
    #     """获取并行处理层级"""
    #     if not nx.is_directed_acyclic_graph(self.graph):
    #         raise ValueError("依赖图中存在循环依赖，无法确定处理顺序")
    #
    #     levels = []
    #     remaining_graph = self.graph.copy()
    #
    #     while remaining_graph.nodes():
    #         # 找出当前层级（入度为0的节点）
    #         current_level = set()
    #         for node in remaining_graph.nodes():
    #             if remaining_graph.in_degree(node) == 0:
    #                 current_level.add(node)
    #
    #         if not current_level:
    #             break
    #
    #         levels.append(current_level)
    #
    #         # 移除当前层级的节点，准备处理下一层级
    #         remaining_graph.remove_nodes_from(current_level)
    #
    #     return levels
    #
    # def print_parallel_processing_levels(self):
    #     # 获取并行处理层级
    #     levels = self.get_parallel_processing_levels()
    #
    #     # 打印结果
    #     print("并行处理层级:")
    #     for i, level in enumerate(levels, 1):
    #         print(f"Level {i}: {sorted(level)}")
